﻿using DynamicAuthorization.Sdk.Attributes;
using DynamicAuthorization.Sdk.Model;
using System.Reflection;

namespace DynamicAuthorization.Sdk.Utils;

public static class PermissionSniffer
{
    private static IEnumerable<Type> _controllers = Enumerable.Empty<Type>();

    public static void Initialize(Assembly assembly)
    {
        var externalTypes = assembly.GetReferencedAssemblies()
            .SelectMany(x =>
                Assembly.Load(x)
                .GetExportedTypes()
                .Where(n => n.Name.EndsWith("Controller")));

        _controllers = assembly
            .GetExportedTypes()
            .Where(n => n.Name.EndsWith("Controller"))
            .Union(externalTypes);
    }

    public static IEnumerable<ApiPermissionInfo> GetAllApiPermissions()
    {
        var types = _controllers
            .ToDictionary(k => k.Name, v => v.GetMethods().Where(n => n.GetCustomAttribute<DynamicAuthorizeAttribute>() != null))
            .Where(n => n.Value.Any())
            .Select(x =>
            {
                var info = new ApiPermissionInfo { Controller = x.Key[..^10] };
                var controllerDefinition = _controllers.First(n => n.Name == x.Key).GetCustomAttribute<PermisstionDefinitionAttribute>();
                if (controllerDefinition is not null)
                {
                    info.ControllerDisplayName = controllerDefinition.DisplayName;
                }

                info.ApiInfo = x.Value
                    .Where(x => x.GetCustomAttribute<PermisstionDefinitionAttribute>() != null)
                    .ToDictionary(
                        k => $"{x.Key[..^10]}:{k.Name}({string.Join(',', k.GetParameters().Select(n => n.ParameterType.Name))})",
                        v => v.GetCustomAttribute<PermisstionDefinitionAttribute>()!.DisplayName);

                return info;
            });

        return types;
    }

    public static IEnumerable<RoutePermissionInfo> GetAllRoutePermissions()
    {
        var routes = _controllers
            .Where(n => n.GetCustomAttribute<PermisstionDefinitionAttribute>() != null)
            .Select(n =>
            {
                var controllerDefinition = n.GetCustomAttribute<PermisstionDefinitionAttribute>()!;

                return new RoutePermissionInfo
                {
                    Controller = n.Name,
                    ControllerDisplayName = controllerDefinition.DisplayName,
                    Name = controllerDefinition.Route
                };
            })
            .Where(n => !string.IsNullOrEmpty(n.Name));

        return routes;
    }

    public static IEnumerable<string> ToApiPermissionInfo(this IEnumerable<string> source)
    {
        var infos = new List<string>();

        var mapCache = new Dictionary<string, IEnumerable<MethodInfo>>();

        if (source.Any(n => n == "#"))
        {
            infos = GetAllApiPermissions().Select(n => n.ApiInfo).SelectMany(n => n.Keys).ToList();
        }
        else
        {
            foreach (var item in source.Where(n => !n.StartsWith("~")))
            {
                var dataSlice = item.Split(":");
                var controller = _controllers.FirstOrDefault(n => n.Name == $"{dataSlice[0]}Controller");

                if (controller is not null)
                {
                    if (!mapCache.TryGetValue(controller.Name, out var methodsCache))
                    {
                        methodsCache = controller.GetMethods().Where(n => n.GetCustomAttribute<DynamicAuthorizeAttribute>() != null);
                        mapCache.Add(controller.Name, methodsCache);
                    }

                    var method = methodsCache.FirstOrDefault(n => dataSlice[1] == $"{n.Name}({string.Join(',', n.GetParameters().Select(x => x.ParameterType.Name))})");

                    if (method is not null)
                    {
                        var methodDefinition = method.GetCustomAttribute<PermisstionDefinitionAttribute>();
                        var value = $"{controller.Name[..^10]}:{method.Name}({string.Join(',', method.GetParameters().Select(n => n.ParameterType.Name))})";
                        var exists = infos.FirstOrDefault(n => n == value);

                        if (exists is null)
                        {
                            infos.Add(value);
                        }
                    }
                }
            }
        }

        foreach (var item in source.Where(n => n != "#" && n.StartsWith("~")))
        {
            var api = item[1..];

            var dataSlice = api.Split(":");
            var controller = _controllers.FirstOrDefault(n => n.Name == $"{dataSlice[0]}Controller");

            if (controller is not null)
            {
                if (!mapCache.TryGetValue(controller.Name, out var methodsCache))
                {
                    methodsCache = controller.GetMethods().Where(n => n.GetCustomAttribute<DynamicAuthorizeAttribute>() != null);
                    mapCache.Add(controller.Name, methodsCache);
                }

                var method = methodsCache.FirstOrDefault(n => dataSlice[1] == $"{n.Name}({string.Join(',', n.GetParameters().Select(x => x.ParameterType.Name))})");

                if (method is not null)
                {
                    var methodDefinition = method.GetCustomAttribute<PermisstionDefinitionAttribute>();
                    var value = $"{controller.Name[..^10]}:{method.Name}({string.Join(',', method.GetParameters().Select(n => n.ParameterType.Name))})";
                    var exists = infos.FirstOrDefault(n => n == value);

                    if (exists is not null)
                    {
                        infos.Remove(exists);
                    }
                }
            }
        }

        return infos;
    }

    public static IEnumerable<string> ToRoutePermissionInfo(this IEnumerable<string> source)
    {
        var infos = new List<string>();

        if (source.Any(n => n == "#"))
        {
            infos = GetAllRoutePermissions().Select(n => n.Name).ToList();
        }
        else
        {
            foreach (var item in source.Where(n => !n.StartsWith("~")))
            {
                var controller = _controllers
                    .Where(n => n.GetCustomAttribute<PermisstionDefinitionAttribute>() != null)
                    .FirstOrDefault(n => n.GetCustomAttribute<PermisstionDefinitionAttribute>()!.Route == item);

                if (controller is not null)
                {
                    var controllerDefinition = controller.GetCustomAttribute<PermisstionDefinitionAttribute>()!;

                    infos.Add(controllerDefinition.Route);
                }
            }
        }

        foreach (var item in source.Where(n => n != "#" && n.StartsWith("~")))
        {
            var route = item[1..];

            var exists = infos.FirstOrDefault(n => n == item[1..]);

            if (exists is not null)
            {
                infos.Remove(exists);
            }
        }

        return infos;
    }

    public static IEnumerable<string> FromApiPermissionInfo(this IEnumerable<string> source)
    {
        var maps = _controllers
            .ToDictionary(k => k.Name, v => v.GetMethods().Where(n => n.GetCustomAttribute<DynamicAuthorizeAttribute>() != null))
            .Where(n => n.Value.Any());

        var fullPermsMethodsFromController = new List<string>();

        var types = source.Select(x =>
        {
            if (x.Contains('('))
            {
                //var controller = maps.FirstOrDefault(n => n.Value.Any(m => $"{n.Key[..^10]}:{m.Name}({string.Join(',', m.GetParameters().Select(x => x.ParameterType.Name))})" == x));

                return x;
            }
            else
            {
                var controller = $"{x}Controller";

                fullPermsMethodsFromController.AddRange(
                    maps
                        .First(n => n.Key == controller)
                        .Value
                        .Select(m => $"{x}:{m.Name}({string.Join(',', m.GetParameters().Select(j => j.ParameterType.Name))})"));

                return string.Empty;
            }
        }).Where(n => !string.IsNullOrEmpty(n)).ToList();

        return types.Union(fullPermsMethodsFromController);
    }

    public static List<string> AnalyzeViewRoutes(this IEnumerable<string> source)
    {
        var maps = _controllers
            .ToDictionary(k => k, v => v.GetMethods().Where(n => n.GetCustomAttribute<DynamicAuthorizeAttribute>() != null))
            .Where(n => n.Value.Any());

        var routes = source.Select(x =>
        {
            Type controller = null!;

            if (x.Contains('('))
            {
                controller = maps.First(n => n.Key.Name == $"{x.Split(':')[0]}Controller").Key;
            }
            else
            {
                controller = maps.First(n => n.Key.Name == $"{x}Controller").Key;
            }

            var definition = controller.GetCustomAttribute<PermisstionDefinitionAttribute>();

            return definition is not null ? definition.Route : $"/{controller.Name.ToLower()}";
        }).Distinct().ToList();

        return routes;
    }
}